﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using ParallelRuntimeLibrary.Internal;
using System.Threading;

namespace ParallelRuntimeLibrary
{
    public static class Parallel
    {// This class still under-test

        public static void TestFor(int FromIncluded, int ToExcluded)
        {
            int All = ToExcluded - FromIncluded;
            if (All == 0) return;

            // 1- Get processors number that assign for this process, and calc the work pieces
            uint PCount = 5;

            if (PCount == 1)
            {
                for (int i = FromIncluded; i < ToExcluded; i++)
                    Console.WriteLine(i.ToString());
            }
            else if (All <= PCount)
            {
                string paralleLoopId = NativeMethod.GetTickCount().ToString() + RandomEx.NextDouble().ToString();

                int o = 0;
                for (int i = FromIncluded; i < ToExcluded; i++, o++)
                    Console.WriteLine(i.ToString());

            }
            else
            {
                string paralleLoopId = NativeMethod.GetTickCount().ToString() + RandomEx.NextDouble().ToString();

                int p = All / (int)PCount, f = FromIncluded, t = FromIncluded;

                for (int i = 0; i < PCount; i++)
                {
                    f = t;
                    t = (i + 1 == PCount) ? ToExcluded : f + p;

                    Console.WriteLine("From " + f + " > " + t);
                }

            }
        }

        public static void TestFor(int FromIncluded, int ToExcluded, int Step)
        {
            float TO = ToExcluded, FROM = FromIncluded;
            int all = ((TO - FROM) / Step) > (ToExcluded - FromIncluded) / Step ?
                ((ToExcluded - FromIncluded) / Step) + 1 : (ToExcluded - FromIncluded) / Step;

            if (all == 0) return;

            // 1- Get processors number that assign for this process, and calc the work pieces
            uint PCount = TaskManager.Scheduler.PricessorCount;

            if (PCount == 1)
            {
                for (int i = FromIncluded; i < ToExcluded; i += Step)
                    Console.WriteLine(i.ToString());
            }
            else if (all <= PCount)
            {
                int o = 0;
                for (int i = FromIncluded; i < ToExcluded; i += Step, o++)
                    Console.WriteLine(i.ToString());
            }
            else
            {
                // Template variables for the calculation.
                float LOOPCOUNT = (TO - FROM) / Step;
                int LoopCount = (int)(LOOPCOUNT), pp;

                LOOPCOUNT = LoopCount = LOOPCOUNT > LoopCount ? LoopCount + 1 : LoopCount;
                int p = (int)((LOOPCOUNT / PCount) * Step), f = FromIncluded, t = FromIncluded + p;

                for (uint i = 0; ; i++)
                {
                    // 3- Create a delgate that will work into different thread

                    Console.WriteLine("From " + f + " > " + t);
                    if (i + 1 == PCount) break;

                    // Calc the new f;
                    f = t;
                    TO = --t;
                    if (((TO - FROM) / Step) != ((t - FromIncluded) / Step))
                    {
                        float tmp = ((TO - FROM) / Step) - ((t - FromIncluded) / Step);
                        f = t + (Step - Convert.ToInt32(tmp * Step));
                    }
                    else
                        f = t + Step;

                    pp = (p - (f - (t + 1)));
                    t = f + pp > ToExcluded ? ToExcluded : f + pp;
                    t = t + 1 == ToExcluded ? ToExcluded : t;
                    t = i + 1 == PCount ? ToExcluded : t;
                }
            }


        }

        /// <summary>
        /// Parallel For
        /// </summary>
        /// <param name="FromIncluded">Start from this number (From Included)</param>
        /// <param name="ToExcluded">End at this number Excluded</param>
        /// <param name="Body">Your Code</param>
        public static void For(int FromIncluded, int ToExcluded, Action<int> Body)
        {
            int All = ToExcluded - FromIncluded;
            if (All == 0) return;

            // 1- Get processors number that assign for this process, and calc the work pieces
            uint PCount = TaskManager.Scheduler.PricessorCount;

            if (PCount == 1)
            {
                try
                {
                    for (int i = FromIncluded; i < ToExcluded; i++)
                        Body(i);
                }
                catch (ThreadAbortException)
                {
                    return;
                }
            }
            else if (All <= PCount)
            {
                string paralleLoopId = NativeMethod.GetTickCount().ToString() + RandomEx.NextDouble().ToString();

                CountdownEvent waitHandle = new CountdownEvent(All);
                Task[] tasks = new Task[All];

                int o = 0;
                for (int i = FromIncluded; i < ToExcluded; i++, o++)
                    tasks[o] = new ForLoopTask<int>(paralleLoopId, Body, i, i + 1, 1, null, waitHandle, null);

                TaskManager.Start(tasks);
                waitHandle.Wait();
            }
            else
            {
                string paralleLoopId = NativeMethod.GetTickCount().ToString() + RandomEx.NextDouble().ToString();

                int p = All / (int)PCount, f = FromIncluded, t = FromIncluded;
                Task[] tasks = new Task[PCount];
                CountdownEvent waitHandle = new CountdownEvent((int)PCount);

                for (int i = 0; i < PCount; i++)
                {
                    f = t;
                    t = (i + 1 == PCount) ? ToExcluded : f + p;

                    tasks[i] = new ForLoopTask<int>(paralleLoopId, Body, f, t, 1, null, waitHandle, null);
                }

                TaskManager.Start(tasks);
                waitHandle.Wait();
            }


        }

        /// <summary>
        /// Parallel For
        /// </summary>
        /// <param name="FromIncluded">Start from this number (From Included)</param>
        /// <param name="ToExcluded">End at this number Excluded</param>
        /// <param name="Body">Your Code</param>
        public static void For<T>(int FromIncluded, int ToExcluded, Func<T> localInit, Action<int, T> Body)
        {
            int All = ToExcluded - FromIncluded;
            if (All == 0) return;

            // 1- Get processors number that assign for this process, and calc the work pieces
            uint PCount = TaskManager.Scheduler.PricessorCount;

            if (PCount == 1)
            {
                try
                {
                    var tmp = localInit();

                    for (int i = FromIncluded; i < ToExcluded; i++)
                        Body(i, tmp);
                }
                catch (ThreadAbortException)
                {
                    return;
                }
            }
            else if (All <= PCount)
            {
                string paralleLoopId = NativeMethod.GetTickCount().ToString() + RandomEx.NextDouble().ToString();

                CountdownEvent waitHandle = new CountdownEvent(All);
                Task[] tasks = new Task[All];

                int o = 0;
                for (int i = FromIncluded; i < ToExcluded; i++, o++)
                    tasks[o] = new ForLoopTask<T>(paralleLoopId, Body, i, i + 1, 1, localInit, waitHandle, null);

                TaskManager.Start(tasks);
                waitHandle.Wait();
            }
            else
            {
                string paralleLoopId = NativeMethod.GetTickCount().ToString() + RandomEx.NextDouble().ToString();

                int p = All / (int)PCount, f = FromIncluded, t = FromIncluded;
                Task[] tasks = new Task[PCount];
                CountdownEvent waitHandle = new CountdownEvent((int)PCount);

                for (int i = 0; i < PCount; i++)
                {
                    f = t;
                    t = (i + 1 == PCount) ? ToExcluded : f + p;

                    tasks[i] = new ForLoopTask<T>(paralleLoopId, Body, f, t, 1, localInit, waitHandle, null);
                }

                TaskManager.Start(tasks);
                waitHandle.Wait();
            }


        }

        /// <summary>
        /// Parallel For
        /// </summary>
        /// <param name="FromIncluded">Start from this number (From Included)</param>
        /// <param name="ToExcluded">End at this number Excluded</param>
        /// <param name="Body">Your Code</param>
        public static void For(int FromIncluded, int ToExcluded, Action<int, ParallelLoopState> Body)
        {
            int All = ToExcluded - FromIncluded;
            if (All == 0) return;

            // 1- Get processors number that assign for this process, and calc the work pieces
            uint PCount = TaskManager.Scheduler.PricessorCount;

            if (PCount == 1)
            {
                try
                {
                    for (int i = FromIncluded; i < ToExcluded; i++)
                        Body(i, ParallelLoopState.CreateSimple());
                }
                catch (ThreadAbortException)
                {
                    return;
                }
            }
            else if (All <= PCount)
            {
                string paralleLoopId = NativeMethod.GetTickCount().ToString() + RandomEx.NextDouble().ToString();

                CountdownEvent waitHandle = new CountdownEvent(All);
                ParallelLoopState pls = new ParallelLoopState();
                Task[] tasks = new Task[All];

                int o = 0;
                for (int i = FromIncluded; i < ToExcluded; i++, o++)
                    tasks[o] = new ForLoopTask<int>(paralleLoopId,Body, i, i + 1, 1, null, waitHandle, pls);

                pls.mTasks = tasks;
                TaskManager.Start(tasks);
                waitHandle.Wait();
            }
            else
            {
                string paralleLoopId = NativeMethod.GetTickCount().ToString() + RandomEx.NextDouble().ToString();

                int p = All / (int)PCount, f = FromIncluded, t = FromIncluded;
                ParallelLoopState pls = new ParallelLoopState();
                Task[] tasks = new Task[PCount];
                CountdownEvent waitHandle = new CountdownEvent((int)PCount);

                for (int i = 0; i < PCount; i++)
                {
                    f = t;
                    t = (i + 1 == PCount) ? ToExcluded : f + p;

                    tasks[i] = new ForLoopTask<int>(paralleLoopId, Body, f, t, 1, null, waitHandle, pls);
                }

                pls.mTasks = tasks;
                TaskManager.Start(tasks);
                waitHandle.Wait();
            }


        }

        /// <summary>
        /// Parallel For
        /// </summary>
        /// <param name="FromIncluded">Start from this number (From Included)</param>
        /// <param name="ToExcluded">End at this number Excluded</param>
        /// <param name="Body">Your Code</param>
        public static void For<T>(int FromIncluded, int ToExcluded, Func<T> localInit, Action<int, T, ParallelLoopState> Body)
        {
            int All = ToExcluded - FromIncluded;
            if (All == 0) return;

            // 1- Get processors number that assign for this process, and calc the work pieces
            uint PCount = TaskManager.Scheduler.PricessorCount;

            if (PCount == 1)
            {
                try
                {
                    var tmp = localInit();

                    for (int i = FromIncluded; i < ToExcluded; i++)
                        Body(i, tmp, ParallelLoopState.CreateSimple());
                }
                catch (ThreadAbortException)
                {
                    return;
                }
            }
            else if (All <= PCount)
            {
                string paralleLoopId = NativeMethod.GetTickCount().ToString() + RandomEx.NextDouble().ToString();

                CountdownEvent waitHandle = new CountdownEvent(All);
                ParallelLoopState pls = new ParallelLoopState();
                Task[] tasks = new Task[All];

                int o = 0;
                for (int i = FromIncluded; i < ToExcluded; i++, o++)
                    tasks[o] = new ForLoopTask<T>(paralleLoopId, Body, i, i + 1, 1, localInit, waitHandle, pls);

                pls.mTasks = tasks;
                TaskManager.Start(tasks);
                waitHandle.Wait();
            }
            else
            {
                string paralleLoopId = NativeMethod.GetTickCount().ToString() + RandomEx.NextDouble().ToString();

                int p = All / (int)PCount, f = FromIncluded, t = FromIncluded;
                ParallelLoopState pls = new ParallelLoopState();
                Task[] tasks = new Task[PCount];
                CountdownEvent waitHandle = new CountdownEvent((int)PCount);

                for (int i = 0; i < PCount; i++)
                {
                    f = t;
                    t = (i + 1 == PCount) ? ToExcluded : f + p;

                    tasks[i] = new ForLoopTask<T>(paralleLoopId, Body, f, t, 1, localInit, waitHandle, pls);
                }

                pls.mTasks = tasks;
                TaskManager.Start(tasks);
                waitHandle.Wait();
            }


        }

        /// <summary>
        /// Parallel For
        /// </summary>
        /// <param name="FromIncluded">Start from this number (From Included)</param>
        /// <param name="ToExcluded">End at this number Excluded</param>
        /// <param name="Step">Step execute your code every</param>
        /// <param name="Body">Your Code</param>
        public static void For(int FromIncluded, int ToExcluded, int Step, Action<int, ParallelLoopState> Body)
        {
            float TO = ToExcluded, FROM = FromIncluded;
            int all = ((TO - FROM) / Step) > (ToExcluded - FromIncluded) / Step ?
                ((ToExcluded - FromIncluded) / Step) + 1 : (ToExcluded - FromIncluded) / Step;

            if (all == 0) return;

            // 1- Get processors number that assign for this process, and calc the work pieces
            uint PCount = TaskManager.Scheduler.PricessorCount;
            
            if (PCount == 1)
            {
                try
                {
                    for (int i = FromIncluded; i < ToExcluded; i += Step)
                        Body(i, ParallelLoopState.CreateSimple());
                }
                catch (ThreadAbortException)
                {
                    return;
                }
            }
            else if (all <= PCount)
            {
                string paralleLoopId = NativeMethod.GetTickCount().ToString() + RandomEx.NextDouble().ToString();
                ParallelLoopState pls = new ParallelLoopState();
                Task[] tasks = new Task[all];
                CountdownEvent waitHandle = new CountdownEvent(all);

                int o = 0;
                for (int i = FromIncluded; i < ToExcluded; i+= Step, o++)
                    tasks[o] = new ForLoopTask<int>(paralleLoopId, Body, i, i + 1, Step, null, waitHandle, pls);

                pls.mTasks = tasks;

                TaskManager.Start(tasks);
                waitHandle.Wait();
            }
            else
            {
                string paralleLoopId = NativeMethod.GetTickCount().ToString() + RandomEx.NextDouble().ToString();

                // Template variables for the calculation.
                float LOOPCOUNT = (TO - FROM) / Step;
                int LoopCount = (int)(LOOPCOUNT), pp;

                LOOPCOUNT = LoopCount = LOOPCOUNT > LoopCount ? LoopCount + 1 : LoopCount;
                int p = (int)((LOOPCOUNT / PCount) * Step), f = FromIncluded, t = FromIncluded + p;

                ParallelLoopState pls = new ParallelLoopState();
                Task[] tasks = new Task[PCount];
                CountdownEvent waitHandle = new CountdownEvent((int)PCount);

                for (uint i = 0; ; i++)
                {
                    // 3- Create a delgate that will work into different thread

                    tasks[i] = new ForLoopTask<int>(paralleLoopId, Body, f, t, Step, null, waitHandle, pls);
                    if (i + 1 == PCount) break;

                    // Calc the new f;
                    f = t;
                    TO = --t;
                    if (((TO - FROM) / Step) != ((t - FromIncluded) / Step))
                    {
                        float tmp = ((TO - FROM) / Step) - ((t - FromIncluded) / Step);
                        f = t + (Step - Convert.ToInt32(tmp * Step));
                    }
                    else
                        f = t + Step;

                    pp = (p - (f - (t + 1)));
                    t = f + pp > ToExcluded ? ToExcluded : f + pp;
                    t = t + 1 == ToExcluded ? ToExcluded : t;
                    t = i + 1 == PCount ? ToExcluded : t;
                }

                pls.mTasks = tasks;
                TaskManager.Start(tasks);
                waitHandle.Wait();
            }


        }
    }
}
